import React, {Component} from 'react';
import Markdown from 'react-markdown';
import {Anchor, Row, Col} from 'antd';

const {Link} = Anchor;

class JsPage extends Component {
    constructor(props) {
        super(props);
    }


    render() {

        return (
            <div style={{background:"#c0d2bc"}}>
                <Row>
                    <Col span={19}>
                        <p>spring多数据源解决方案</p>
                        <h3 id="s1"> 单库事务控制 </h3>
                        <Markdown source={"大多数项目只需要一个事务管理器。然而，有些项目为了提高效率、或者有多个完全不同又不相干的数据源，最好用多个事务管理器。机智的Spring的Transactional管理已经考虑到了这一点，首先分别定义多个transactional manager，并为qualifier属性指定不同的值；然后在需要使用@Transactional注解的时候指定TransactionManager的qualifier属性值或者直接使用bean名称。配置和代码使用的例子：\n" +
                        "\n" +
                        "    <tx:annotation-driven/>\n" +
                        "    \n" +
                        "    <bean id=\"transactionManager1\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n" +
                        "        <property name=\"dataSource\" ref=\"datasource1\"></property>\n" +
                        "        <qualifier value=\"datasource1Tx\"/>\n" +
                        "    </bean>\n" +
                        "    \n" +
                        "    <bean id=\"transactionManager2\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n" +
                        "        <property name=\"dataSource\" ref=\"datasource2\"></property>\n" +
                        "        <qualifier value=\"datasource2Tx\"/>\n" +
                        "    </bean>\n" +
                        "\n" +
                        "使用时，用@Transactional(\"datasource1Tx\")和@Transactional(\"datasource2Tx\")，来区别具体使用某个事务管理器\n" +
                        "\n" +
                        "    public class TransactionalService {\n" +
                        "    \n" +
                        "        @Transactional(\"datasource1Tx\")\n" +
                        "        public void setSomethingInDatasource1() { ... }\n" +
                        "    \n" +
                        "        @Transactional(\"datasource2Tx\")\n" +
                        "        public void doSomethingInDatasource2() { ... }\n" +
                        "    }\n" +
                        "    \n" +
                        "或者，直接使用transactin manager 的bean名字：\n" +
                        "\n" +
                        "    @Transactional(\"transactionManager1\")   \n" +
                        "\n" +
                        "如果是使用@Transactional()，相当于使用缺省的transaction mananger名字，即：@Transactional(\"transactionManager\")\n"}/>
                        <h3 id="s2"> 数据源切换 </h3>
                        <Markdown source={"在spring配置文件中配置这两个数据源： \n" +
                        "数据源1\n" +
                        "    \n" +
                        "    <!-- initialSize初始化时建立物理连接的个数0  maxActive最大连接池数量8 minIdle最小连接池数量0-->\n" +
                        "    <bean id=\"datasource1\" class=\"com.alibaba.druid.pool.DruidDataSource\" scope=\"singleton\">\n" +
                        "        <property name=\"username\" value=\"${jdbc.username}\" />\n" +
                        "        <property name=\"password\" value=\"${jdbc.password}\" />\n" +
                        "        <property name=\"url\" value=\"${jdbc.url}\" />\n" +
                        "        <property name=\"initialSize\" value=\"${jdbc.init}\" />\n" +
                        "        <property name=\"maxActive\" value=\"${jdbc.max}\" />\n" +
                        "        <property name=\"minIdle\" value=\"${jdbc.min}\" />\n" +
                        "    </bean>\n" +
                        "\n" +
                        "数据源2\n" +
                        "\n" +
                        "    <bean id=\"datasource2\" class=\"com.alibaba.druid.pool.DruidDataSource\" scope=\"singleton\">\n" +
                        "        <property name=\"username\" value=\"${jdbc.username2}\" />\n" +
                        "        <property name=\"password\" value=\"${jdbc.password2}\" />\n" +
                        "        <property name=\"url\" value=\"${jdbc.url2}\" />\n" +
                        "        <property name=\"initialSize\" value=\"${jdbc.init2}\" />\n" +
                        "        <property name=\"maxActive\" value=\"${jdbc.max2}\" />\n" +
                        "        <property name=\"minIdle\" value=\"${jdbc.min2}\" />\n" +
                        "    </bean>\n" +
                        "    \n" +
                        "使用自己实现的数据源实现类MultipleDataSource\n" +
                        "\n" +
                        "    <bean id=\"multipleDataSource\" class=\"com.xian.datasource.MultipleDataSource\">\n" +
                        "        <!-- 设置默认的数据源 -->\n" +
                        "        <property name=\"defaultTargetDataSource\" ref=\"dataSourceOne\"/>\n" +
                        "        <property name=\"targetDataSources\">\n" +
                        "            <map>\n" +
                        "                <!-- 这个key是对应数据源的别称，通过这个key可以找到对应的数据源，value-ref就是上面数据源的id -->\n" +
                        "                <entry key=\"datasource1\" value-ref=\"datasource1\"/>\n" +
                        "                <entry key=\"datasource2\" value-ref=\"datasource2\"/>\n" +
                        "                <entry key=\"datasource3\" value-ref=\"datasource3\"/>\n" +
                        "            </map>\n" +
                        "        </property>\n" +
                        "    </bean>\n" +
                        "\n" +
                        "spring使用多数据源\n" +
                        "\n" +
                        "    <bean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\">\n" +
                        "        <property name=\"dataSource\" ref=\"multipleDataSource\"/>\n" +
                        "        <property name=\"configLocation\" value=\"classpath:mybatis.xml\" />\n" +
                        "        <property name=\"mapperLocations\">\n" +
                        "            <list>\n" +
                        "                <value>classpath:cn/xian/**/*-mapper.xml</value>\n" +
                        "            </list>\n" +
                        "        </property>\n" +
                        "\n" +
                        "    </bean>\n" +
                        "\n" +
                        "mybatis.spring自动映射\n" +
                        "\n" +
                        "    <bean class=\"org.mybatis.spring.mapper.MapperScannerConfigurer\">\n" +
                        "        <property name=\"basePackage\" value=\"com.xian.mapper\"/>\n" +
                        "    </bean>\n" +
                        "\n" +
                        "自动扫描\n" +
                        "\n" +
                        "    <context:component-scan base-package=\"com.xian.**\"/>\n" +
                        "    \n" +
                        "事务控制\n" +
                        "\n" +
                        "    <aop:config>\n" +
                        "        <aop:pointcut id=\"mutipoint\" expression=\"execution(* com.xian.service.*.*(..))\" />\n" +
                        "        <aop:advisor advice-ref=\"txAdvice\" pointcut-ref=\"mutipoint\" />  \n" +
                        "    </aop:config></beans>\n"}/>


                        <h3 id="s3"> 数据源集合 </h3>
                        <Markdown source={"MultipleDataSource :\n" +
                        "\n" +
                        "    package com.xian.datasource;\n" +
                        "    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;\n" +
                        "    \n" +
                        "    /**\n" +
                        "     * 多数据源java实现\n" +
                        "     */\n" +
                        "    public class MultipleDataSource extends AbstractRoutingDataSource {\n" +
                        "        private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();\n" +
                        "    \n" +
                        "        public static void setDataSourceKey(String dataSource) {\n" +
                        "            dataSourceKey.set(dataSource);\n" +
                        "        }\n" +
                        "    \n" +
                        "        @Override\n" +
                        "        protected Object determineCurrentLookupKey() {\n" +
                        "            return dataSourceKey.get();\n" +
                        "        }\n" +
                        "    }\n" +
                        "\n" +
                        " _\n" +
                        "    \n" +
                        "    package com.xian.controller;\n" +
                        "    \n" +
                        "    import org.apache.log4j.Logger;\n" +
                        "    import org.aspectj.lang.ProceedingJoinPoint;\n" +
                        "    import org.aspectj.lang.annotation.Around;\n" +
                        "    import org.aspectj.lang.annotation.Aspect;\n" +
                        "    import org.aspectj.lang.annotation.Pointcut;\n" +
                        "    import org.springframework.core.annotation.Order;\n" +
                        "    import org.springframework.stereotype.Component;\n" +
                        "    import com.xian.datasource.MultipleDataSource;\n" +
                        "    import com.xian.service.DataBase1;\n" +
                        "    import com.xian.service.DataBase2;\n" +
                        "    import com.xian.service.DataBase3;\n" +
                        "    \n" +
                        "    /**\n" +
                        "     * 数据库链接自动切换AOP处理\n" +
                        "     * Order优先级设置到最高，因为在所有service方法调用前都必须把数据源确定\n" +
                        "     * Order数值越小优先级越高\n" +
                        "     */\n" +
                        "    @Component\n" +
                        "    @Aspect\n" +
                        "    @Order(1)\n" +
                        "    public class MultipleDataSourceAspectAdvice {\n" +
                        "        private static final Logger LOGGER = Logger.getLogger(MultipleDataSourceAspectAdvice.class);\n" +
                        "    \n" +
                        "        public MultipleDataSourceAspectAdvice() {\n" +
                        "            LOGGER.info(\"MultipleDataSourceAspectAdvice 加载成功\");\n" +
                        "        }\n" +
                        "    \n" +
                        "        /**\n" +
                        "         * 定义切面\n" +
                        "         */\n" +
                        "        @Pointcut(\"execution(* com.xian.service.*.*(..))\")\n" +
                        "        public void pointCut() {} \n" +
                        "    \n" +
                        "        @Around(\"pointCut()\")\n" +
                        "        public Object doAround(ProceedingJoinPoint jp) throws Throwable {\n" +
                        "            if (jp.getTarget() instanceof DataBase1) {\n" +
                        "                LOGGER.debug(\"使用数据库链接：dataSourceOneKey\");\n" +
                        "                MultipleDataSource.setDataSourceKey(\"dataSourceOneKey\");\n" +
                        "            } else if (jp.getTarget() instanceof DataBase2) {\n" +
                        "                LOGGER.debug(\"使用数据库链接：dataSourceTwoKey\");\n" +
                        "                MultipleDataSource.setDataSourceKey(\"dataSourceTwoKey\");\n" +
                        "            } else if (jp.getTarget() instanceof DataBase3) {\n" +
                        "                LOGGER.debug(\"使用数据库链接：dataSourceThreeKey\");\n" +
                        "                MultipleDataSource.setDataSourceKey(\"dataSourceThreeKey\");\n" +
                        "            } else {            // 默认是dataSourceOneKey\n" +
                        "                LOGGER.debug(\"使用数据库链接：dataSourceOneKey\");\n" +
                        "                MultipleDataSource.setDataSourceKey(\"dataSourceOneKey\");\n" +
                        "            }\n" +
                        "            return jp.proceed();\n" +
                        "        }\n" +
                        "    }\n" +
                        "_    \n" +
                        "    \n" +
                        "    package com.cnblogs.service;\n" +
                        "    public interface DataBase1 {\n" +
                        "\n" +
                        "    }\n" +
                        "    \n" +
                        "    package com.cnblogs.service;\n" +
                        "    public interface DataBase2 {\n" +
                        "\n" +
                        "    }\n" +
                        "    \n" +
                        "    package com.cnblogs.service;\n" +
                        "    public interface DataBase3 {\n" +
                        "\n" +
                        "    }\n" +
                        "到这里spring多数据源配置已经完毕，那如何在执行service方法时让service切换到正确的数据库呢？上面的类中定义了有3个类DataBase1，DataBase2，DataBase3，这3个类其实只是一个interface，没有任何实现方法，我们让具体业务的service都继承至这3个类以区分不同的service对应不同的数据源，因为业务的service我是知道他用的哪个数据源的，比如FooService继承至DataBase1，则在使用FooService任何方法时AOP就会先把数据源切换到dataSourceOneKey，以此就达到了自动切换数据源的目的，并且支持事务;\n"}/>

                        <h3 id="s4"> spring 的AbstractRoutingDataSource </h3>
                        <Markdown source={"spring 的AbstractRoutingDataSource  抽象类 该类相当于数据源的路由中介 能在程序运行的过程中根据key 动态的指定使用数据源。\n" +
                        "\n" +
                        "spring 源码：\n" +
                        "\n" +
                        "public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}；\n" +
                        "\n" +
                        "由此可以看出该类主要继承 AbstractDataSource  同时AbstractDataSource  又是javax.sql.DataSource 的子类  \n" +
                        "\n" +
                        "从AbstractRoutingDataSource 的getConnection（）方法着手\n" +
                        "\n" +
                        "    @Override\n" +
                        "    public Connection getConnection() throws SQLException {\n" +
                        "        return determineTargetDataSource().getConnection();\n" +
                        "    }\n" +
                        "    @Override\n" +
                        "    public Connection getConnection(String username, String password) throws SQLException {\n" +
                        "        return determineTargetDataSource().getConnection(username, password);\n" +
                        "\n" +
                        "    }\n" +
                        "    protected DataSource determineTargetDataSource() {\n" +
                        "        Assert.notNull(this.resolvedDataSources, \"DataSource router not initialized\");\n" +
                        "        Object lookupKey = this.determineCurrentLookupKey();\n" +
                        "        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);\n" +
                        "        if (dataSource == null) {\n" +
                        "            dataSource = this.resolvedDefaultDataSource;\n" +
                        "        }\n" +
                        "\n" +
                        "        if (dataSource == null) {\n" +
                        "            throw new IllegalStateException(\"Cannot determine target DataSource for lookup key [\" + lookupKey + \"]\");\n" +
                        "        } else {\n" +
                        "            return dataSource;\n" +
                        "        }\n" +
                        "    }\n" +
                        "    \n" +
                        "段源码的重点在于determineCurrentLookupKey()方法，这是AbstractRoutingDataSource类中的一个抽象方法，而它的返回值是你所要用的数据源dataSource的key值，有了这个key值，resolvedDataSources（这是个map,由配置文件中设置好后存入的）就从中取出对应的DataSource，如果找不到，就用配置默认的数据源。\n" +
                        "\n" +
                        "原理大致如下 这时候可以扩展AbstractRoutingDataSource类，并重写其中的determineCurrentLookupKey()方法，来实现数据源的切换\n"}/>

                    </Col>
                    <Col span={5}>
                        <Anchor showInkInFixed="true" style={{background:"#ebefe0"}}>
                            <Link href="#s1" title="单库事务控制"/>
                            <Link href="#s2" title="数据源切换"/>
                            <Link href="#s3" title="数据源集合"/>
                            <Link href="#s4" title="spring 的AbstractRoutingDataSource"/>
                        </Anchor>
                    </Col>
                </Row>
            </div>

        );
    }
}

export default JsPage;